"use strict";

const chalk = require("chalk");
const electron = require("electron");
const path = require("path");
const { say } = require("cfonts");
const { spawn } = require("child_process");
const webpack = require("webpack");
const WebpackDevServer = require("webpack-dev-server");
const webpackHotMiddleware = require("webpack-hot-middleware");

const mainConfig = require("./webpack.main.config");
const rendererConfig = require("./webpack.renderer.config");

let electronProcess = null;
let manualRestart = false;
let hotMiddleware;

function logStats(proc, data) {
	let log = "";

	log += chalk.yellow.bold(
		`┏ ${proc} Process ${new Array(19 - proc.length + 1).join("-")}`
	);
	log += "\n\n";

	if (typeof data === "object") {
		data.toString({
			colors: true,
			chunks: false,
		})
			.split(/\r?\n/)
			.forEach((line) => {
				log += "  " + line + "\n";
			});
	} else {
		log += `  ${data}\n`;
	}

	log += "\n" + chalk.yellow.bold(`┗ ${new Array(28 + 1).join("-")}`) + "\n";

	console.log(log);
}

function startRenderer() {
	return new Promise((resolve, reject) => {
		rendererConfig.entry.renderer = [
			path.join(__dirname, "dev-client"),
		].concat(rendererConfig.entry.renderer);
		rendererConfig.mode = "development";
		const compiler = webpack(rendererConfig);
		hotMiddleware = webpackHotMiddleware(compiler, {
			log: false,
			heartbeat: 2500,
		});

		compiler.hooks.compilation.tap("compilation", (compilation) => {
			compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync(
				"html-webpack-plugin-after-emit",
				(data, cb) => {
					hotMiddleware.publish({ action: "reload" });
					cb();
				}
			);
		});

		compiler.hooks.done.tap("done", (stats) => {
			logStats("Renderer", stats);
		});

		const server = new WebpackDevServer(compiler, {
			contentBase: path.join(__dirname, "../"),
			quiet: true,
			hot: true,
			before(app, ctx) {
				app.use(hotMiddleware);
				ctx.middleware.waitUntilValid(() => {
					resolve();
				});
			},
		});

		server.listen(9080);
	});
}

function startMain() {
	return new Promise((resolve, reject) => {
		mainConfig.entry.main = [
			path.join(__dirname, "../src/main/index.dev.js"),
		].concat(mainConfig.entry.main);
		mainConfig.mode = "development";
		const compiler = webpack(mainConfig);

		compiler.hooks.watchRun.tapAsync("watch-run", (compilation, done) => {
			logStats("Main", chalk.white.bold("compiling..."));
			hotMiddleware.publish({ action: "compiling" });
			done();
		});

		compiler.watch({}, (err, stats) => {
			if (err) {
				console.log(err);
				return;
			}

			logStats("Main", stats);

			if (electronProcess && electronProcess.kill) {
				manualRestart = true;
				process.kill(electronProcess.pid);
				electronProcess = null;
				startElectron();

				setTimeout(() => {
					manualRestart = false;
				}, 5000);
			}

			resolve();
		});
	});
}

function startElectron() {
	var args = [
		"--inspect=5858",
		path.join(__dirname, "../dist/electron/main.js"),
	];

	// detect yarn or npm and process commandline args accordingly
	if (process.env.npm_execpath.endsWith("yarn.js")) {
		args = args.concat(process.argv.slice(3));
	} else if (process.env.npm_execpath.endsWith("npm-cli.js")) {
		args = args.concat(process.argv.slice(2));
	}

	electronProcess = spawn(electron, args);

	electronProcess.stdout.on("data", (data) => {
		electronLog(data, "blue");
	});
	electronProcess.stderr.on("data", (data) => {
		electronLog(data, "red");
	});

	electronProcess.on("close", () => {
		if (!manualRestart) process.exit();
	});
}

function electronLog(data, color) {
	let log = "";
	data = data.toString().split(/\r?\n/);
	data.forEach((line) => {
		log += `  ${line}\n`;
	});
	if (/[0-9A-z]+/.test(log)) {
		console.log(
			chalk[color].bold("┏ Electron -------------------") +
				"\n\n" +
				log +
				chalk[color].bold("┗ ----------------------------") +
				"\n"
		);
	}
}

function greeting() {
	const cols = process.stdout.columns;
	let text = "";

	if (cols > 104) text = "electron-vue";
	else if (cols > 76) text = "electron-|vue";
	else text = false;

	if (text) {
		say(text, {
			colors: ["yellow"],
			font: "simple3d",
			space: false,
		});
	} else console.log(chalk.yellow.bold("\n  electron-vue"));
	console.log(chalk.blue("  getting ready...") + "\n");
}

function init() {
	greeting();

	Promise.all([startRenderer(), startMain()])
		.then(() => {
			startElectron();
		})
		.catch((err) => {
			console.error(err);
		});
}

init();
